<?php
/**
 * Easthv Zend Framework Project
 *
 *
 * LICENSE:
 *
 * @category   Zend
 * @package    Easthv_Model
 * @copyright  Copyright (c) 2009-2010 Easthv Team @ East Hui Van Business Consulting Co., Ltd.(http://www.easthv.com)
 * @license    http://framework.zend.com/license/   BSD License
 * @version    1.0
 * @link       http://www.easthv.com
 * @since      1.0
*/

/**
 * Model Basic Class
 * 
 * @uses       Easthv_Model_DbTable_DB
 * @category   Zend
 * @package    Easthv_Model
 * @copyright  Copyright (c) 2009-2010 Easthv Team @ East Hui Van Business Consulting Co., Ltd.(http://www.easthv.com)
 * @license    http://framework.zend.com/license/   BSD License
 * @version    1.0
 * @link       http://www.easthv.com
 * @since      1.0
 * @deprecated
 */
abstract class Easthv_Model_Basic
{

    /**
     * @var string Model class prefix
     */
    const prefix = "Easthv_";

    /**
     * @var object Zend_Db_Table_Abstract
     */
    protected $_dbTable;

    /**
     * @var array validation messages of one table
     */
    protected $_validMessage;

    /**
     * @var array validation messages of multi tables
     */
    protected $_validMessages;


    /**
     * @var array  fields in this array will not be valid
     */
    protected $_validException;


    /**
     * @var string  table class name
     */
    protected $_tableClass;


    /**
     * @var array   Paginator info array
     */
    protected $_pager = array(
        'total' => null,
        'page' => null,
        'pages' => null,
        'rows' => null,
        'pagesize' => null,
        'prev' => null,
        'forshort_prev' => null,
        'next' => null,
        'forshort_next' => null,
        'numbers' => null,
        'offset_start' => null,
        'offset_end' => null,
    );

    /**
     * @var array  
     */
    protected $_queryCache;


	protected $_frontView = false;

	public function setFrontView(Zend_View $view)
	{
		$this->_frontView = &$view;
	}

	public function getFrontView()
	{
		return $this->_frontView;
	}
    /**
     * Returns a result array from database
     *
     * find array format:
     * array(
     *  'from' => array ('id','name')  encapsulation of Zend_Db_Select from function
     *  'where' => array ('configuration = ?' , 'value')  encapsulation of Zend_Db_Select where function
     *  'or' => array ('configuration = ?' , 'value')     encapsulation of Zend_Db_Select orWhere function   
     *  'group' => array ('configuration')  encapsulation of Zend_Db_Select group function
     *  'having' => array ('configuration = ?' , 'value')  encapsulation of Zend_Db_Select having function
     *  'orHaving' => array ('configuration = ?' , 'value')  encapsulation of Zend_Db_Select orHaving function
        'distinct' => boolean
     *  'page' => 1    page number  
     *  'sortby' => 'id'    sortby condition   
     *  'order' => 'ASC'    select order   
     *  'order' => array("id ASC, date DESC")  order could be array if need multi orders
     *  'rows' => 'row number'
     *  'noLimit' => false    if true,page option will be of no effect,all items in table will be return without limit
     *  'noPager' => false    if true, result rows pager will not be created
     *  'cache' => array(
     *      'key' => 'unique key value',
     *      'lifeTime' => 7200 , lifetime seconds
     *  ),
     *  'has' => array('ClassName','referenceRule'),  Has One relationship reference class name
     *  'bls' => array('ClassName','referenceRule'),  Belongs To relationship reference class name
     *  'o2m' => array('ClassName','referenceRule'),  One To Many relationship reference class name
     *  'm2m' => array('referenceClassName','relationshipClassName','referenceRule','relationshipRule'),  Many To Many relationship reference class name
     * )

     * @param mixed    $type
     *                             if type is string. will be type of find operation (all | one | count)
     *                             if type is int. will return primary_key = $type
     *                             if type is array. will be a quick SQL conditions array, example : array('id = ?',1)
     * @param array    $find    select condition array
     *
     * @access public
     *
     * @return array   result set array
     */
    public function find($type = null, $find = null)
    {
        if($find) {
            $findOptions = array(
                'from' => null,    
                'fromTable' => null,    
                'where' => null,    
                'or' => null,    
                'group' => null,
                'distinct' => null,    
                'having' => null,    
                'orHaving' => null,    
				'join' => null,
                'page' => null,    
                'sortby' => null,    
                'order' => null,    
                'rows' => null,    
                'noLimit' => null,    
                'noPager' => null,    
                'cache' => array(
                    'key' => null,    
                    'lifeTime' => null,    
                ),    
                'has' => null,    
                'bls' => null,    
                'o2m' => null,    
                'm2m' => null,    
            );

            foreach($find as $key => $value){
                $findOptions[$key] = $value;
            }
            $find = $findOptions;
        }

        if (isset($find['cache']['key'])) {

            $key = $find['cache']['key'];

            if (!$key) {
                throw new Exception('Cache key not set');
            }

            $cache = $this->initQueryCache();

            if ($cache) {

                if (isset($find['cache']['lifeTime']) && $find['cache']['lifeTime'] == -1) {
                    $cache->remove($key);
                    return $this->_find($type,$find);
                }

                if (isset($find['cache']['lifeTime']) && $find['cache']['lifeTime'] > 0) {
                    $cache->setLifetime($find['cache']['lifeTime']);
                }

                if (!($data = $cache->load($key))) {
                    $data = $this->_find($type,$find);
                    $cache->save(array(
                        'data' => $data,
                        'pager' => $this->_pager,
                    ));
                } else {
                    $this->_pager = $data['pager'];
                    return $data['data'];
                }

            }
        }

        return $this->_find($type, $find);
    }

    /**
     * Returns a result set array from database.
     *
     * find array format:
     * array(
     *  'from' => array ('id','name')  encapsulation of Zend_Db_Select from function
     *  'fromTable' => array('p' => 'products'), | 'p'
     *  'where' => array ('configuration = ?' , 'value')  encapsulation of Zend_Db_Select where function
     *  'or' => array ('configuration = ?' , 'value')     encapsulation of Zend_Db_Select orWhere function   
     *  'group' => array ('configuration')  encapsulation of Zend_Db_Select group function
     *  'having' => array ('configuration = ?' , 'value')  encapsulation of Zend_Db_Select having function
     *  'orHaving' => array ('configuration = ?' , 'value')  encapsulation of Zend_Db_Select orHaving function
     *  'join' => array('joinTableName','condition = condition','*')
     *  'page' => 1    page number  
     *  'sortby' => 'id'    sortby condition   
     *  'order' => 'ASC'    select order   
     *  'order' => array("id ASC, date DESC")  order could be array if need multi orders
     *  'rows' => 'row number'
     *  'noLimit' => false    if true,page option will be of no effect,all items in table will be return without limit
     *  'noPager' => false    if true, result rows pager will not be created
     *  'cache' => array(
     *      'key' => 'something',
     *      'lifeTime' => 7200 , lifetime seconds
     *  ),
     *  'has' => array('ClassName','referenceRule'),  Has One relationship reference class name
     *  'bls' => array('ClassName','referenceRule'),  Belongs To relationship reference class name
     *  'o2m' => array('ClassName','referenceRule'),  One To Many relationship reference class name
     *  'm2m' => array('referenceClassName','relationshipClassName','referenceRule','relationshipRule'),  Many To Many relationship reference class name
     * )

     * @param mixed    $type
     *                             if type is string. will be type of find operation (all | one | count)
     *                             if type is int. will return primary_key = $type
     *                             if type is array. will be a quick SQL conditions array, example : array('id = ?',1)
     * @param array    $find    select condition array
     *
     * @access private
     *
     * @return array   result set array
     */
    private function _find($type = null, $find = null)
    {
        $query = $this->_dbTable->select();
        $primary = $this->_dbTable->getPrimaryKey();
        $primary = is_array($primary) ? $primary[1] : $primary;

        //get by id
        if (is_numeric($type)) {

            if (!$primary) {

                throw new Exception('No primary key found.');

            }

            $query->where("$primary = ?", $type);
            $row = $this->_dbTable->fetchRow($query);

            return $row->$primary > 0 ? $row->toArray() : array();

        }

        //quick by one condition
        if (is_array($type)) {

            $query->where($type[0], $type[1]);
            $row = $this->_dbTable->fetchRow($query);

            return $row ? $row->toArray() : array();            
        }

        $type = strtolower((string)$type);

        //FROM must be the first part
		if ($find['from']) {

			if($find['join']){
				$fromTable = $find['fromTable'] ? $find['fromTable'] : array(
					$this->getTableName() => $this->getTableName()
				);
				$query->from($fromTable, $find['from']);
			} else {
				$fromTable = $find['fromTable'] ? $find['fromTable'] : $this->getTableName();
				$query->from($fromTable, $find['from']);
			}
        }


		if($find['join']) {

			$query->setIntegrityCheck(false); 
			
			if(is_array($find['join'][0])) {
				foreach($find['join'] as $join){
					$joinCondition = array();

					if(isset($join[2]) && is_string($join[2])){
						$joinCondition[] = $join[2];
					}
					if(is_array($join[2])){
						$joinCondition = $join[2];
					}

					$query->join(array(
						$join[0] => $join[0]
					),$join[1], $joinCondition);				
				}

			} else {
				$joinCondition = array();

				if(isset($find['join'][2]) && is_string($find['join'][2])){
					$joinCondition[] = $find['join'][2];
				}
				if(is_array($find['join'][2])){
					$joinCondition = $find['join'][2];
				}


				$query->join(array(
					$find['join'][0] => $find['join'][0]
				),$find['join'][1], $joinCondition);			
			}
		}

		if ($find['distinct']) {
			$query->distinct();
		}

		if ($find['where']) {

			if (!is_array($find['where'])) {

				throw new Exception("Error format in FIND function where condition");

            }

            if (isset($find['where'][0]) && !is_array($find['where'][0])) {

                $query->where($find['where'][0],$find['where'][1]);

            } else {
                foreach($find['where'] as $where) {

                    $query->where($where[0],$where[1]);

                }            
            }

        }

        if ($find['or']) {

            if (!is_array($find['or'])) {
                throw new Exception("Error format in FIND function or condition");
            }

            if (isset($find['or'][0]) && !is_array($find['or'][0])) {

                $query->orWhere($find['or'][0],$find['or'][1]);

            }  else {

                foreach($find['or'] as $where) {

                    $query->orWhere($where[0],$where[1]);

                }
            }

        }

        if ($find['group']) {
            $query->group($find['group']);
        }

        if ($find['having']) {

            if (!is_array($find['having'])) {

                throw new Exception("Error format in FIND function having condition");

            }

            if (isset($find['having'][0]) && !is_array($find['having'][0])) {

                $query->having($find['having'][0],$find['having'][1]);

            } else {

                foreach($find['having'] as $having) {

                    $query->having($having[0],$having[1]);

                }            
            }

        }

        if ($find['orHaving']) {

            if (!is_array($find['orHaving'])) {

                throw new Exception("Error format in FIND function orHaving condition");

            }

            if (isset($find['orHaving'][0]) && !is_array($find['orHaving'][0])) {

                $query->orHaving($find['orHaving'][0],$find['orHaving'][1]);

            } else {
                foreach($find['orHaving'] as $having) {

                    $query->orHaving($having[0],$having[1]);

                }            
            }

        }


        $order = $this->getQueryOrder($find['page'],$find['sortby'],$find['order'],$find['rows']);
        if ($order['order']) {
			$query->order($order['order']);
		} else {
            if($primary) {
                $query->order(" $primary DESC ");
            }
		}


        if (!$find['noPager'] && !$find['noLimit'] && $type != 'one') {

            $count = clone $query;
            $primary = $primary ? $primary : '*';
            //Reset from first
			$count->reset('from');

			if($find['join']){
				if ($find['distinct']) {
					$count->from($this->getTableName(), "count(DISTINCT {$this->getTableName()}.$primary) AS count");
				} else {
					$count->from($this->getTableName(), "count({$this->getTableName()}.$primary) AS count");
				}
				$count->setIntegrityCheck(false); 
				if(is_array($find['join'][0])) {
					foreach($find['join'] as $join){
						$joinCondition = array();

						if(isset($join[2]) && is_string($join[2])){
							$joinCondition[] = $join[2];
						}
						if(is_array($join[2])){
							$joinCondition = $join[2];
						}

						$count->join(array(
							$join[0] => $join[0]
						),$join[1], $joinCondition);				
					}

				} else {
					$joinCondition = array();

					if(isset($find['join'][2]) && is_string($find['join'][2])){
						$joinCondition[] = $find['join'][2];
					}
					if(is_array($find['join'][2])){
						$joinCondition = $find['join'][2];
					}


					$count->join(array(
						$find['join'][0] => $find['join'][0]
					),$find['join'][1], $joinCondition);			
				}
			} else {
				if ($find['distinct']) {
					$count->from($this->getTableName(), "count(DISTINCT {$this->getTableName()}.$primary) AS count");
				} else {
					$count->from($this->getTableName(), "count({$this->getTableName()}.$primary) AS count");
				}
			}

			$countArr = $this->_dbTable->fetchRow($count);
			$countArr = $countArr ? $countArr->toArray() : array();
			

			if ($countArr['count'] <= 0) {

				$this->_pager['total'] = 0;

				return $type == 'count' ? 0 : array();

			} else {

				$this->_pager['total'] = $countArr['count'];

                if ($type == 'count') {
                    return $countArr['count'];
                }

            }

        }


        if (!$find['noLimit']) {
            $query->limit($order['rows'],$order['offset']);
        }

        $rows = $type == 'one' ? $this->_dbTable->fetchRow($query) : $this->_dbTable->fetchAll($query);

        if(!$rows) {
            return array();
        }

        $res = $rows->toArray();



        //ONE2MANY relationships
        if ($find['o2m']) {

            $rule = null;
            $relTable = array();
            $relTableClass = array();

            //only one relationship and no param
            if (!is_array($find['o2m'])) {

                $relTable = $find['o2m'];
                $relTableClass = self::prefix . 'Model_DbTable_' . $relTable;

            } else {

                //many relationships
                if (is_array($find['o2m'][0])) {

                    foreach($find['o2m'] as $rel) {

                        $relTable[] = $rel[0];

                        $relTableClass[] = self::prefix . 'Model_DbTable_' . $rel[0];

                        $rule[] = $rel[1];

                    }


                } else {

                    $relTable = $find['o2m'][0];
                    $relTableClass = self::prefix . 'Model_DbTable_' . $relTable;
                    $rule = $find['o2m'][1];

                }



            }

            if ($type == 'one') {

                if(is_array($relTable)) {

                    foreach($relTable as $key => $_relTable) {

                        $rel = $rows->findDependentRowset($relTableClass[$key],$rule[$key]);

                        $res[$_relTable] = $rel->toArray();

                    }


                } else {

                    $rel = $rows->findDependentRowset($relTableClass,$rule);

                    $res[$relTable] = $rel->toArray();                

                }


            } else {


                if (is_array($relTable)) {

                    foreach($relTable as $_key => $_relTable) {

                        foreach($rows as $key => $row) {

                            $rel = $row->findDependentRowset($relTableClass[$_key],$rule[$_key]);

                            $res[$key][$_relTable] = $rel->toArray();

                        }

                    }


                } else {

                    foreach($rows as $key => $row) {

                        $rel = $row->findDependentRowset($relTableClass,$rule);

                        $res[$key][$relTable] = $rel->toArray();

                    }            

                }

            }

        }


        //Has one relationships
        if ($find['has']) {

            $rule = null;
            
            $relTable = array();
            $relTableClass = array();

            //only one relationship and no param
            if (!is_array($find['has'])) {

                $relTable = $find['has'];
                $relTableClass = self::prefix . 'Model_DbTable_' . $relTable;
            
            } else {

                //many relationships
                if (is_array($find['has'][0])) {

                    foreach($find['has'] as $rel) {

                        $relTable[] = $rel[0];
                        
                        $relTableClass[] = self::prefix . 'Model_DbTable_' . $rel[0];

                        $rule[] = $rel[1];
                    
                    }
                
                
                } else {

                    $relTable = $find['has'][0];
                    $relTableClass = self::prefix . 'Model_DbTable_' . $relTable;
                    $rule = $find['has'][1];
                
                }
                
            }

            if ($type == 'one') {

                if (is_array($relTable)) {

                    foreach($relTable as $key => $_relTable) {

                        $rel = $rows->findDependentRowset($relTableClass[$key],$rule[$key]);
                        $rel = $rel ? $rel->toArray() : array();
                        $rel = $rel ? $rel[0] : array();
                        $res[$_relTable] = $rel;

                    }


                } else {

                    $rel = $rows->findDependentRowset($relTableClass,$rule);
                    $rel = $rel ? $rel->toArray() : array();
                    $rel = $rel ? $rel[0] : array();
                    $res[$relTable] = $rel;    

                }


            } else {


                if (is_array($relTable)) {

                    foreach($relTable as $_key => $_relTable) {

                        foreach($rows as $key => $row) {

                            $rel = $row->findDependentRowset($relTableClass[$_key],$rule[$_key]);
                            $rel = $rel ? $rel->toArray() : array();
                            $rel = $rel ? $rel[0] : array();
                            $res[$key][$_relTable] = $rel;

                        }

                    }


                } else {

                    foreach($rows as $key => $row) {

                        $rel = $row->findDependentRowset($relTableClass,$rule);
                        $rel = $rel ? $rel->toArray() : array();
                        $rel = $rel ? $rel[0] : array();
                        $res[$key][$relTable] = $rel;

                    }            

                }

            }

        }


        //Belongsto relationships
        if ($find['bls']) {

            $rule = null;
            $relTable = array();
            $relTableClass = array();

            //only one relationship and no param
            if (!is_array($find['bls'])) {

                $relTable = $find['bls'];
                $relTableClass = self::prefix . 'Model_DbTable_' . $relTable;
            
            } else {

                //many relationships
                if(is_array($find['bls'][0])) {

                    foreach($find['bls'] as $rel) {

                        $relTable[] = $rel[0];
                        
                        $relTableClass[] = self::prefix . 'Model_DbTable_' . $rel[0];

                        $rule[] = isset($rel[1]) ? $rel[1] : null;
                    
                    }
                
                } else {

                    $relTable = $find['bls'][0];
                    $relTableClass = self::prefix . 'Model_DbTable_' . $relTable;
                    $rule = $find['bls'][1];
                
                }
                
            }

            if ($type == 'one') {

                if (is_array($relTable)) {

                    foreach($relTable as $key => $_relTable) {

                        $rel = $rows->findParentRow($relTableClass[$key],$rule[$key]);

                        $res[$_relTable] = $rel ? $rel->toArray() : array();

                    }


                } else {

                    $rel = $rows->findParentRow($relTableClass,$rule);

                    $res[$relTable] = $rel ? $rel->toArray() : array();    

                }


            } else {


                if (is_array($relTable)) {

                    foreach($relTable as $_key => $_relTable) {

                        foreach($rows as $key => $row) {

                            $rel = $row->findParentRow($relTableClass[$_key],$rule[$_key]);

                            $res[$key][$_relTable] = $rel ? $rel->toArray() : array();

                        }

                    }


                } else {

                    foreach($rows as $key => $row) {

                        $rel = $row->findParentRow($relTableClass,$rule);

                        $res[$key][$relTable] = $rel ? $rel->toArray() : array();

                    }            

                }
            }
        }


        //MANY2MANY relationships
        if ($find['m2m']) {

            $rule1 = null;
            $rule2 = null;
            $relTable = array();
            $relTableClass = array();

            //many relationships
            if (is_array($find['m2m'][0])) {

                foreach($find['m2m'] as $rel) {

                    $table[] = $rel[0];
                    $relTable[] = $rel[1];
                    $tableClass[] = self::prefix . 'Model_DbTable_' . $rel[0];
                    $relTableClass[] = self::prefix . 'Model_DbTable_' . $rel[1];
                    $rule1[] = $rel[2];
                    $rule2[] = $rel[3];

                }
            
            
            } else {

                $table = $find['m2m'][0];
                $relTable = $find['m2m'][1];
                $tableClass = self::prefix . 'Model_DbTable_' . $table;
                $relTableClass = self::prefix . 'Model_DbTable_' . $relTable;
                $rule1 = $find['m2m'][2];
                $rule2 = $find['m2m'][3];

            }

            if ($type == 'one') {

                if (is_array($table)) {

                    foreach($table as $key => $_table) {

                        $rel = $rows->findManyToManyRowset($tableClass[$key],$relTableClass[$key],$rule1[$key],$rule2[$key]);

                        $res[$_table] = $rel ? $rel->toArray() : array();

                    }


                } else {

                    $rel = $rows->findManyToManyRowset($tableClass,$relTableClass,$rule1,$rule2);

                    $res[$table] = $rel ? $rel->toArray() : array();        

                }


            } else {


                if (is_array($table)) {

                    foreach($table as $_key => $_table) {

                        foreach($rows as $key => $row) {

                            $rel = $row->findManyToManyRowset($tableClass[$_key],$relTableClass[$_key],$rule1[$_key],$rule2[$_key]);
                            $res[$key][$_table] = $rel ? $rel->toArray() : array();

                        }

                    }


                } else {

                    foreach($rows as $key => $row) {

                        $rel = $row->findManyToManyRowset($tableClass,$relTableClass,$rule1,$rule2);
                        $res[$key][$table] = $rel ? $rel->toArray() : array();

                    }            

                }



            }

    
        
        }

        return $res;
    }

    /**
     * Execute a sql query
     *
     * @access public
     * @param string $sql
     *
     * @return object
     */
    public function query($sql = null)
    {
        if (!$sql) {
            return;
        }

        return $this->_dbTable->getAdapter()->query($sql);
    }


    /**
     * Begin Transaction
     *
     * @access public
     *
     * @return void
     */
    public function begin()
    {
        $this->_dbTable->getAdapter()->beginTransaction();
    }

    /**
     * Datebase commit
     *
     * @access public
     *
     * @return void
     */
    public function commit()
    {
        $this->_dbTable->getAdapter()->commit();
    }

    /**
     * Datebase rollback
     *
     * @access public
     *
     * @return void
     */
    public function rollback()
    {
        $this->_dbTable->getAdapter()->rollBack();
    }

    /**
     * Add additional validation rules array to model
     * 
     *
     * @access public
     * @param array rules
     *
     * @return void
     */
    public function addValidRules($rules)
    {
        if (!$rules) {
            throw new Exception("No validation rule need to add");
        }

        foreach($rules as $rulename => $rule){
            $this->addValidRule($rulename,$rule);
        }
    }
    
    /**
     * Add single validation rule array to model
     * 
     *
     * @access public
     * @param string rulename
     * @param string $rule
     *
     * @return void
     */
    public function addValidRule($rulename, $rule = null)
    {
        if (!$rulename) {
            throw new Exception("No validation rule name.");
        }

        $valid = $this->_dbTable->getValidate();
        $valid[$rulename] = $rule;
        $this->_dbTable->setValidate($valid);

    }
    
    /**
     * Remove single validation rule from model
     * 
     *
     * @access public
     * @param string rulename
     *
     * @return void
     */
    public function removeValidRule($rulename)
    {
        if (!$rulename) {
            throw new Exception("No validation rule name.");
        }

        $valid = $this->_dbTable->getValidate();
        unset($valid[$rulename]);
        $this->_dbTable->setValidate($valid);    
    }

    /**
     * Get validation messages
     *
     * @access public
     *
     * @return array     validation messages
     */
    public function getValidMessage()
    {
        return $this->_validMessage;
    }


    /**
     * Get validation messages (multi rows)
     *
     * @access public
     *
     * @return array     validation messages
     */
    public function getValidMessages()
    {
        return $this->_validMessages;
    }
    
    /**
     * Set a new valid exception
     *
     * @access public
     * @param array $ex
     *
     * @return void
     */
    public function setValidateException($ex)
    {
        $this->_validException = $ex;
    }


    /**
     * validate one row data
     *
     * @param array    $inputArr    data to be validate
     *
     * @access public
     *
     * @return boolean  if validation passed return true, failed return false
     */
    public function validate($inputArr = null)
    {
        $this->_validMessage = array();

        if (!$inputArr) {
            return true;
        }

        $res = true;
        $rules = $this->_dbTable->getValidate();

        foreach($inputArr as $key => $input){
            //not set validation rule
            if (!isset($rules[$key])) {
                continue;
            }

            $notEmpty = false;
            foreach($rules[$key] as $_key => $rule) {
                if (isset($rule['exception']) && $this->_validException) {
                    $rules[$key][$_key]['exception'] = $this->_validException;
                }
                if ($rule['rule'] == 'NotEmpty') {
                    $notEmpty = true;
                }
            }

            //allow Empty, no check
            if ($notEmpty === false && ($input == null || $input == '')) {
                continue;
            }

            $resone = Zend_Easthv_Validate::validates($input,$rules[$key]);
            $this->_validMessage[$key] = Zend_Easthv_Validate::getMessages();

            if ($resone === false) {
                $res = false;
            }
        }


        return $res;
    }


    /**
     * validate multi rows data
     *
     * @param array    $input    data to be validate
     * @param array    $rules    validation rules
     *
     * @access public
     *
     * @return boolean  if validation passed return true, failed return false
     */
    public function validates($inputs = null)
    {
        if (!$inputs) {

            return true;

        }

        $res = true;
        foreach($inputs as $key => $input) {

            if (!$input) {
                continue;
            }
    
            if ($this->validate($input) === false) {

                $res = false;

                $this->_validMessages[$key] = $this->_validMessage;
            
            }
        
        }

        return $res;
    }


    /**
     * insert input data into DB
     *
     * @param array    $input    data to be insert
     *
     * @access public
     *
     * @return int last insert id
     */
    public function add($input)
    {
        if (!$input || !is_array($input)) {
            throw new Exception('add input error');
        }
        
        $this->_dbTable->insert($input);
            
        return $this->getLastInsertId();
    }

    /**
     * update DB data
     *
     * @param int       $id       update data id
     * @param array     $data     data to be update
     * @param string    $field    DB field name
     *
     * @access public
     *
     * @return boolean  if update successed return true
     */
    public function update($input, $data)
    {
        if (!$input || !$data || !is_array($data)) {
            throw new Exception('update input error');
        }

        $primary = $this->_dbTable->getPrimaryKey();
        $primary = is_array($primary) ? $primary[1] : $primary;
        $adapter = $this->_dbTable->getAdapter();

        if (is_numeric($input)) {

            $where = $adapter->quoteInto("$primary = ?", $input);

        } else {

            if (!is_array($input)) {

                throw new Exception('Update input format incorrect.');

            }

            $where = array();

            if (is_array($input[0])) {

                foreach($input as $cond) {

                    $where[] = $adapter->quoteInto($cond[0], $cond[1]);

                }

                $where = $where ? implode(' AND ',$where) : '';

            } else {

                $where = $adapter->quoteInto($input[0], $input[1]);

            }

        }

        $this->_dbTable->update($data, $where);

        return true;

    }


    /**
     * delete DB data
     *
     * @param mixed     $input    if input is a number, will be item id
     *                            if input is an array, will be select condition      
     * @param string    $field    DB field name
     *
     * @access public
     *
     * @return boolean  if delete successed return true
     */
    public function delete($input)
    {
        if (!$input) {

            throw new Exception('delete input error');

        }

        $primary = $this->_dbTable->getPrimaryKey();
        $primary = is_array($primary) ? $primary[1] : $primary;
        $adapter = $this->_dbTable->getAdapter();

        if (is_numeric($input)) {

            $where = $adapter->quoteInto("$primary = ?", $input);

        } else {

            if (!is_array($input)) {

                throw new Exception('Delete input format incorrect.');

            }

            $where = array();

            if (is_array($input[0])) {

                foreach($input as $cond) {

                    $where[] = $adapter->quoteInto($cond[0], $cond[1]);

                }

                $where = $where ? implode(' AND ',$where) : '';

            } else {

                $where = $adapter->quoteInto($input[0], $input[1]);

            }

        }

        $this->_dbTable->delete($where);

        return true;

    }


    /**
     * Get lastInsertId
     *
     * @access public
     *
     * @return int last insert id
     */
    public function getLastInsertId()
    {
        return $this->_dbTable->getAdapter()->lastInsertId();
    }


    /**
     * Get select orders
     *
     * @param int    $page      select page
     * @param string $sortby    order by
     * @param string $order     ASC or DESC
     * @param int    $rows      select rows number
     *
     * @access public
     *
     * @return array select order array
     */
    public function getQueryOrder($page = 1, $sortby = null, $order = null, $rows = null)
    {
        $page = $page > 0 ? $page : 1;

        $config = Zend_Registry::get('config');
        if (!$rows) {
            $rows = $config['system']['rows']['default'] ? (int)$config['system']['rows']['default'] : 10;
        }

        $this->_pager['pageSize'] = $config['system']['rows']['pageSize'] ? (int)$config['system']['rows']['pageSize'] : 2;
        $this->_pager['rows'] = $rows;
        $this->_pager['page'] = $page;


        $offset = $rows*($page - 1);

        if (is_array($order)) {
            return array(
                'order' => $order,
                'offset' => $offset,
                'rows' =>  $rows
            );
        }

        $order = strtolower($order);
        if ($sortby && $order && ($order == 'desc' || $order == 'asc')) {
            return array(
                'order' => "$sortby $order",
                'offset' => $offset,
                'rows' =>  $rows
            );
        } else {
            return array(
                'order' => '',
                'offset' => $offset,
                'rows' =>  $rows
            );
        }

    }

    /**
     * Get Zend_Db_Table_Abstract object
     *
     * @access public
     *
     * @return object    Zend_Db_Table_Abstract object
     */
    public function getTable()
    {
        return $this->_dbTable;
    }

    /**
     * Get table name
     *
     * @access public
     *
     * @return string    table name
     */
    public function getTableName()
    {
        return $this->_dbTable->getTableName();
    }


    /**
     * Get paginator array
     * use this array + pager.phtml of system view
     * A paginator will be generated
     *
     * @access public
     *
     * @return array     paginator array
     */
    public function getPager()
    {
        if ($this->_pager['pages']) {
            return $this->_pager;
        }

        $total = $this->_pager['total'];
        $rows = $this->_pager['rows'];
        $pagesize = $this->_pager['pageSize'];
        $page = $this->_pager['page'];


        if ($total <= $rows) {
            return false;
        }

        $pages = ceil($total/$rows);

        $i = $page - $pagesize;
        $numbers = array();
        for($i; $i < $page ;$i++) {
            $i > 0 ? $numbers['prev'][] = $i : '';
        }
        //$numbers[] = $page;
        for($i = $page+1; $i < $page + $pagesize + 1 ;$i++) {
            $i <= $pages ? $numbers['next'][] = $i : '';
        }

        $this->_pager = array(
            'total' => $total,          //total items number
            'page' => $page,            //current page number
            'pages' => $pages,          //page total number
            'rows' => $rows,            //rows number of one page
            'pagesize' => $pagesize,    //paginator size 
            'prev' => $page - 1 > 0 ? $page - 1 : false,                 //prev page number
            'forshort_prev' => $page - $pagesize > 1 ? true : false,     //if the distance between first page and current page is large than pagesize , this will be true
            'next' => $page + 1 <= $pages ? $page + 1 : false,           //next page number
            'forshort_next' => $page + $pagesize < $pages ? true : false,//if the distance between last page and current page is large than pagesize , this will be true
            'numbers' => $numbers,                                     //page numbers array of prev pages and next pages
			'offset_start' => ($page - 1) * $rows + 1,
			'offset_end' => $page == $pages ? $total : $page * $rows,
        );


        return $this->_pager;
    
    }

    /**
     * Init query cache
     *
     * @access public
     *
     * @return string
     */
    public function initQueryCache()
    {
        if ($this->_queryCache) {
            return $this->_queryCache;
        }

        $config = Zend_Registry::get('config');
        if (!$config['system']['cache']['enable'] || !$config['system']['cache']['query']) {
            return false;
        }

        $cacheType = $config['system']['cache']['backend']['type'];
        if (!in_array($cacheType,array('File','Sqlite','Memcached','Apc','Xcache','ZendPlatform'))) {
            return false;
        }


        $frontendOptions = array(
            'automatic_serialization' => true
        );

        $backendOptions = array();
        if ($cacheType == 'File') {
            $backendOptions = array(
                'cache_dir' => APPLICATION_PATH . $config['system']['path']['cache']['query']
            );
        }

        if ($cacheType == 'Memcached') {
            
            if ($config['system']['cache']['backend']['memcached']['host']) {
                $hosts = explode(',',$config['system']['cache']['backend']['memcached']['host']);
            }
            
            if ($config['system']['cache']['backend']['memcached']['port']) {
                $ports = explode(',',$config['system']['cache']['backend']['memcached']['port']);
            }
            
            if ($config['system']['cache']['backend']['memcached']['persistent']) {
                $persistents = explode(',',$config['system']['cache']['backend']['memcached']['persistent']);
            }

            if (!$hosts || !$ports || count($hosts) != count($ports)) {
                throw new Exception ('Memcached setting error.' );
            }
            
            $servers = array();

            foreach ($hosts as $key=>$host) {
                $servers[] = array(
                    'host' => $host,
                    'port' => $ports[$key],
                    'persistent' => $persistents[$key] == 1 ? true : false,
                );
            }
    
            $backendOptions = array(
                'servers' => $servers,
                'compression' => $config['system']['cache']['backend']['memcached']['compression'] == 1 ? true : false,
            );
        }

        $this->_queryCache = Zend_Cache::factory('Core', $cacheType, $frontendOptions, $backendOptions);

		return $this->_queryCache;
    }


	public function clearQueryCache($cacheKey)
	{
		$cache = $this->initQueryCache();

		if(!$cache) {
			return false;
		}

		if(is_array($cacheKey)) {
			return $cache->clean(
				Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG,
				$cacheKey
			);
		} else {
			return $cache->remove($cacheKey);
		}
	}

	/**
	 * Construct function
	 *
	 * @return void
	 */
	public function __construct()
	{
		if ($this->_tableClass) {

			$tableClassName = 'Easthv_Model_DbTable_' . $this->_tableClass;
			$this->_dbTable = new $tableClassName;

        }
    }

}
